package cn.schoolwow.quickserver.handler;

import cn.schoolwow.quickserver.domain.Client;
import cn.schoolwow.quickserver.response.HttpStatus;
import cn.schoolwow.quickserver.util.MIMEUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 静态资源处理
 */
public class StaticResourceHandler implements Handler {
    private static Logger logger = LoggerFactory.getLogger(StaticResourceHandler.class);
    /**
     * 默认首页
     * */
    private static String[] indexHtmls = new String[]{"index.html","index.htm","index.jsp","default.html","default.htm"};

    @Override
    public Handler handle(Client client) throws IOException {
        getStaticResourcePath(client);
        if (client.httpResponseMeta.bodyStream.size()==0) {
            return new ControllerHandler();
        }
        handleIfNoneMatch(client);
        handleRanges(client);
        return new HttpResponseHandler();
    }

    /**
     * 支持分段下载
     */
    private void handleRanges(Client client) throws IOException {
        if (client.httpRequestMeta.headers.containsKey("Range")) {
            //TODO 当前部分支持Range语法规则,为start-end这种形式
            String range = client.httpRequestMeta.headers.get("Range").get(0);
            int start = Integer.parseInt(range.substring(range.indexOf("=") + 1, range.indexOf("-")).trim());
            int size = client.httpResponseMeta.bodyStream.size();
            int end = range.endsWith("-") ? size : Integer.parseInt(range.substring(range.indexOf("-") + 1));
            byte[] bytes = client.httpResponseMeta.bodyStream.toByteArray();
            logger.trace("[PARTIAL_CONTENT响应]start:{},end:{},size:{},路径:{}",start,end,size,client.httpRequestMeta.uri);
            client.httpResponseMeta.bodyStream.reset();
            client.httpResponseMeta.bodyStream.write(Arrays.copyOfRange(bytes, start, end+1));
            client.httpResponseMeta.httpStatus(HttpStatus.PARTIAL_CONTENT);
            client.httpResponseMeta.headers.put("Content-Range", Arrays.asList("bytes " + start + "-" + end + "/" + size));
        }
    }

    /**
     * 支持缓存
     *
     * @return 是否返回
     */
    private void handleIfNoneMatch(Client client) {
        if (client.httpRequestMeta.headers.containsKey("If-None-Match")) {
            String nowETag = client.httpResponseMeta.headers.get("ETag").get(0);
            String oldETag = client.httpRequestMeta.headers.get("If-None-Match").get(0);
            if (oldETag.equals(nowETag)) {
                logger.trace("[NOT_MODIFIED响应]路径:{}",client.httpRequestMeta.uri);
                client.httpResponseMeta.httpStatus(HttpStatus.NOT_MODIFIED);
                client.httpResponseMeta.bodyStream.reset();
            }
        }
    }

    /**
     * 获取静态资源路径
     */
    private void getStaticResourcePath(Client client) throws IOException {
        //从类路径获取
        String path = client.serverConfigMeta.staticResourcePathPrefix + client.httpRequestMeta.uri.getPath();
        Path resourcePath = getStaticResourcePath(path,client);
        if(null==resourcePath&&path.endsWith("/")){
            for(String indexHtml:indexHtmls){
                resourcePath = getStaticResourcePath(path+indexHtml,client);
                if(null!=resourcePath){
                    break;
                }
            }
        }
        if(null!=resourcePath){
            client.httpResponseMeta.contentType = Files.probeContentType(resourcePath);
            if(null==client.httpResponseMeta.contentType){
                String fileName = resourcePath.toFile().getName();
                if(fileName.contains(".")){
                    String suffixFileName = fileName.substring(fileName.lastIndexOf(".")+1);
                    client.httpResponseMeta.contentType = MIMEUtil.getMIMEType(suffixFileName);
                }
            }
            String ETag = Long.toHexString(Files.getLastModifiedTime(resourcePath).toMillis()) + "-" + Long.toHexString(Files.size(resourcePath));
            client.httpResponseMeta.headers.put("ETag", Arrays.asList(ETag));
            client.httpResponseMeta.bodyStream.write(Files.readAllBytes(resourcePath));
            //支持分段下载
            client.httpResponseMeta.headers.put("Accept-Ranges", Arrays.asList("bytes"));
            if(resourcePath.getFileName().toString().startsWith("QuickServerTemp_")){
                Files.deleteIfExists(resourcePath);
                logger.trace("[删除临时文件]{}",resourcePath);
            }
        }
    }

    /**
     * 获取静态资源
     * @param path 路径
     * @param client 请求信息
     * */
    private Path getStaticResourcePath(String path, Client client) throws IOException {
        URL url = StaticResourceHandler.class.getResource(path);
        logger.trace("[尝试获取静态资源]路径:{},结果:{}",path,url);
        if (null != url) {
            switch (url.getProtocol()) {
                case "file": {
                    File file = new File(url.getFile());
                    if (file.isFile()) {
                        logger.trace("[文件资源]路径:{}",file.getAbsolutePath());
                        return file.toPath();
                    }
                }
                break;
                case "jar": {
                    JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                    JarFile jarFile = jarURLConnection.getJarFile();
                    if(path.startsWith("/")){
                        path = path.substring(1);
                    }
                    JarEntry jarEntry = jarFile.getJarEntry(path);
                    if (jarEntry == null || jarEntry.isDirectory()) {
                        logger.trace("[jar资源]jar文件不存在或者为文件夹,路径:{},jarEntry:{}",path,jarEntry);
                        return null;
                    }
                    String suffixName = jarEntry.getName().contains(".") ? jarEntry.getName().substring(jarEntry.getName().indexOf(".")) : jarEntry.getName();
                    Path resourcePath = Files.createTempFile("QuickServerTemp_", suffixName);
                    Files.copy(jarFile.getInputStream(jarEntry), resourcePath, StandardCopyOption.REPLACE_EXISTING);
                    logger.trace("[jar资源]临时文件路径:{},jarEntry:{}",resourcePath,jarEntry);
                    return resourcePath;
                }
            }
        }
        //从指定的静态路径中寻找
        for (String staticResourcePath : client.serverConfigMeta.staticResourcePathList) {
            Path resourcePath = Paths.get(staticResourcePath + (staticResourcePath.endsWith("/") ? path : "/" + path));
            logger.trace("[尝试从指定静态文件目录中寻找]路径:{}",resourcePath);
            if (Files.exists(resourcePath) && Files.isRegularFile(resourcePath)) {
                logger.trace("[找到静态文件目录资源]路径:{}",resourcePath);
                return resourcePath;
            }
        }
        return null;
    }
}
